﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

// TOFIX: move it to IMPL

namespace Konst3d.Efficiency.IO
{
    public static class FileStreamExtender // BinaryWriter
    {
        #region Constants

        private const int INT32_SIZE = sizeof(int);
        private const int UINT32_SIZE = sizeof(uint);
        private const int BUFFER_SIZE = 4;

        private readonly static byte[] _buffer = new byte[BUFFER_SIZE];

        #endregion // Constants

        public static int ReadInt32(this FileStream file, long offset = -1, SeekOrigin origin = SeekOrigin.Begin)
        {
            if (offset >= 0)
            {
                file.Seek(offset, origin);
            }

            int red = file.Read(_buffer, 0, INT32_SIZE);

            if (red != INT32_SIZE)
            {
                throw new IOException(String.Format("Failed to read {0} bytes at {1} in {2}. Number of bytes actually red is: {3}.", INT32_SIZE, offset, file.Name, red));
            }

            return BitConverter.ToInt32(_buffer, 0);
        }

        public static void WriteInt32(this FileStream file, int value, long offset = -1, SeekOrigin origin = SeekOrigin.Begin)
        {
            if (offset >= 0)
            {
                file.Seek(offset, origin);
            }

            file.Write(BitConverter.GetBytes(value), 0, INT32_SIZE);
            file.Flush();
        }

        public static uint ReadUInt32(this FileStream file, long offset = -1, SeekOrigin origin = SeekOrigin.Begin)
        {
            if (offset >= 0)
            {
                file.Seek(offset, origin);
            }

            int red = file.Read(_buffer, 0, UINT32_SIZE);

            if (red != UINT32_SIZE)
            {
                throw new IOException(String.Format("Failed to read {0} bytes at {1} in {2}. Number of bytes actually red is: {3}.", UINT32_SIZE, offset, file.Name, red));
            }

            return BitConverter.ToUInt32(_buffer, 0);
        }

        public static void WriteUInt32(this FileStream file, uint value, long offset = -1, SeekOrigin origin = SeekOrigin.Begin)
        {
            if (offset >= 0)
            {
                file.Seek(offset, origin);
            }

            file.Write(BitConverter.GetBytes(value), 0, UINT32_SIZE);
            file.Flush();
        }

        public static DateTime ReadShortDate(this FileStream file, long offset = -1, SeekOrigin origin = SeekOrigin.Begin)
        {
            int year   = file.ReadInt32(offset, origin);
            byte month = (byte)file.ReadByte();
            byte day   = (byte)file.ReadByte();

            return new DateTime(year, month, day);
        }

        public static void WriteShortDate(this FileStream file, DateTime date, long offset = -1, SeekOrigin origin = SeekOrigin.Begin)
        {
            file.WriteInt32(date.Year, offset, origin);
            file.WriteByte((byte)date.Month);
            file.WriteByte((byte)date.Day);
            file.Flush();
        }

        public static void WriteBytes(this FileStream file, byte value, int count, long offset = -1, SeekOrigin origin = SeekOrigin.Begin)
        {
            if (offset >= 0)
            {
                file.Seek(offset, origin);
            }

            var buff = new byte[count];

            for (int i = 0; i < buff.Length; ++i)
            {
                buff[i] = value;
            }

            file.Write(buff, 0, buff.Length);
            file.Flush();
        }

        public static void WriteLenghtPrefixedString(this FileStream file, string str, int maxCellSize, long offset = -1, SeekOrigin origin = SeekOrigin.Begin)
        {
            var buff = Encoding.UTF8.GetBytes(str);

            file.WriteInt32(buff.Length < maxCellSize - 4 ? buff.Length : maxCellSize - 4, offset, origin);
            file.Write(buff, 0, buff.Length < maxCellSize - 4 ? buff.Length : maxCellSize - 4);
            file.Flush();
        }

        public static string ReadLenghtPrefixedString(this FileStream file, long offset = -1, SeekOrigin origin = SeekOrigin.Begin)
        {
            string result = null;
            int byteSize = file.ReadInt32(offset, origin);

            if (byteSize > 0)
            {
                var data = new byte[byteSize];
                long offs = file.Position;

                if (file.Read(data, 0, data.Length) != byteSize)
                {
                    throw new IOException(String.Format("Failed to read {0} bytes starting with {1} from {2}.", byteSize, offs, file.Name));
                }

                result = Encoding.UTF8.GetString(data, 0, data.Length);
            }
            else
            {
                result = String.Empty;
            }

            return result;
        }
    }
}
